import sys
from PyQt6.QtCore import Qt
from PyQt6.QtWidgets import *
import PyQt6.QtGui as QtGui
from PyQt6.QtGui import QColor, QAction, QPixmap, QPalette
from pathlib import Path
import cv2
import numpy as np
from Photo import Photo
from functools import partial


class Judge:
    def __init__(self, newWindow: QDialog, confirm, cancel):
        button1 = QPushButton('Confirm', newWindow)
        button1.clicked.connect(confirm)
        button2 = QPushButton('Cancel', newWindow)
        button2.clicked.connect(cancel)
        hbox = QHBoxLayout()
        hbox.addWidget(button1)
        hbox.addWidget(button2)
        self.hbox = hbox


class Menu:
    def __init__(self, menu: QMenu):
        self.menu = menu

    def addFun(self, name, p, lam):
        action = QAction(name, p)
        action.triggered.connect(lam)
        self.menu.addAction(action)


def changeValue(sld, label):
    text = f'ratio: {sld.value()}'
    label.setText(text)


class PhotoProcessor(QMainWindow):
    def __init__(self):
        super().__init__()

        self.labelImage = None
        self.initUI()

    def initUI(self):
        self.labelImage = QLabel(self)
        self.initMenu()
        self.showMaximized()

    def initMenu(self):
        self.statusBar()

        menuBar = self.menuBar()
        fileMenu = menuBar.addMenu('File')
        editMenu = menuBar.addMenu('Edit')
        self.fileMenu = Menu(fileMenu)
        self.editMenu = Menu(editMenu)
        self.initFileMenu()
        self.initEditMenu()

    def initEditMenu(self):
        self.editMenu.addFun('Rotate', self, lambda: self.noAsk(Photo.rotate))
        self.editMenu.addFun('Vertical Mirror', self, lambda: self.noAsk(Photo.vertical_mirror))
        self.editMenu.addFun('Horizontal Mirror', self, lambda: self.noAsk(Photo.horizontal_mirror))
        self.editMenu.addFun('Resize', self, lambda: self.ask('ratio', 1, 500, 100, Photo.resize))
        # wait for brightness
        self.editMenu.addFun('Lightning', self, lambda: self.ask('value', -100, 100, 0, Photo.lighting))
        # crash
        self.editMenu.addFun('Contrast', self, lambda: self.ask('value', -100, 100, 0, Photo.contrast))
        self.editMenu.addFun('Hue', self, lambda: self.ask('value', 0, 180, 0, Photo.hue))

        blurMenu = self.editMenu.menu.addMenu('Blur')
        self.blurMenu = Menu(blurMenu)
        self.blurMenu.addFun('Blur', self, lambda: self.ask('value', 0, 20, 0, Photo.blur))
        self.blurMenu.addFun('Median_Blur', self, lambda: self.ask('value', 0, 20, 0, Photo.median_blur))
        self.blurMenu.addFun('gaussian_blur', self, lambda: self.ask('value', 0, 20, 0, Photo.gaussian_blur))

        self.editMenu.addFun('Gray', self, lambda: self.noAsk(Photo.to_gray))
        self.editMenu.addFun('Invert', self, lambda: self.noAsk(Photo.invert))

        binaryMenu = self.editMenu.menu.addMenu('Binary')
        self.binaryMenu = Menu(binaryMenu)
        self.binaryMenu.addFun('Binary', self, lambda: self.ask('threshold', 0, 255, 127, Photo.to_binary))
        self.binaryMenu.addFun('Binary_ostu', self, lambda: self.noAsk(Photo.to_binary_ostu))
        self.binaryMenu.addFun('Binary_adaptive', self, lambda: self.noAsk(Photo.to_binary_adaptive))

        self.editMenu.addFun('Sharpen', self, lambda: self.ask('value', 0, 20, 0, Photo.sharpen))
        self.editMenu.addFun('Open', self, lambda: self.noAsk(Photo.open))
        self.editMenu.addFun('Close', self, lambda: self.noAsk(Photo.close))
        self.editMenu.addFun('Scale', self, lambda: self.noAsk(Photo.scale))
        self.editMenu.addFun('Equalize', self, lambda: self.noAsk(Photo.equalizeHist))
        self.editMenu.addFun('DetectAntigen', self, lambda: self.noAsk(Photo.detectQRcode))

    def noAsk(self, func):
        func(self.Image)
        self.display_image()

    def initFileMenu(self):
        self.fileMenu.addFun('Open Image', self, self.openImage)
        self.fileMenu.addFun('Save Image', self, self.saveImage)
        self.fileMenu.addFun('Original Image', self, lambda: self.noAsk(Photo.origin))

    def saveImage(self):
        home_dir = str(Path.home())
        filepath = QFileDialog.getSaveFileName(self, 'Save File', home_dir, 'Image files(*.jpg)')
        if filepath[0]:
            cv2.imwrite(filepath[0], self.Image.img)

    def openImage(self):
        home_dir = str(Path.home())
        filepath = QFileDialog.getOpenFileName(self, 'Open Image', home_dir, "Image files(*.jpg *.png)")

        if filepath[0]:
            self.Image = Photo(filepath[0])
            self.display_image()

    def display_image(self):
        shrink = cv2.cvtColor(self.Image.img, cv2.COLOR_BGR2RGB)

        qt_img = QtGui.QImage(shrink.data,
                              shrink.shape[1],
                              shrink.shape[0],
                              shrink.shape[1] * 3,
                              QtGui.QImage.Format.Format_RGB888)

        w, h = qt_img.width(), qt_img.height()
        f1 = 1.0 * self.width() / w
        f2 = 1.0 * self.height() / h
        factor = min([f1, f2])
        width = int(w * factor)
        height = int(h * factor)

        self.labelImage.setPixmap(QPixmap.fromImage(qt_img).scaled(width, height))

        self.labelImage.resize(width, height)

        # self.image_resize()

    def image_resize(self):
        w, h = self.labelImage.width(), self.labelImage.height()
        f1 = 1.0 * self.width() / w
        f2 = 1.0 * self.height() / h
        factor = min([f1, f2])
        width = int(w * factor)
        height = int(h * factor)
        self.labelImage.resize(width, height)

    def cancel(self):
        self.sender().parent().close()

    def ask(self, text_, min_value, max_value, init, func):
        newWindow = QDialog()
        text = text_ + f': {init}'

        vbox = QVBoxLayout()
        label = QLabel(text, newWindow)
        sld = QSlider(Qt.Orientation.Horizontal, newWindow)
        sld.setMaximum(max_value)
        sld.setMinimum(min_value)
        sld.setTickPosition(QSlider.TickPosition.TicksAbove)
        sld.setSliderPosition(init)
        sld.setFocusPolicy(Qt.FocusPolicy.NoFocus)
        sld.setGeometry(30, 40, 200, 30)
        sld.valueChanged[int].connect(lambda: changeValue(sld, label))

        judge = Judge(newWindow, lambda: self.setValue(sld, func), self.cancel)

        vbox.addWidget(label)
        vbox.addWidget(sld)
        vbox.addLayout(judge.hbox)

        newWindow.setLayout(vbox)
        newWindow.show()
        newWindow.exec()

    def setValue(self, sld, fun):
        a = self.sender()
        b = a.parent()
        b.close()
        fun(self.Image, sld.value())
        self.display_image()


def run():
    app = QApplication(sys.argv)
    pp = PhotoProcessor()
    sys.exit(app.exec())


run()
